package com.merry.rsautils.utils;

import com.merry.rsautils.annotation.FieldWithRsa;
import com.merry.rsautils.annotation.ObjectWithRsa;
import sun.misc.BASE64Decoder;

import javax.crypto.Cipher;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;

/**
 * @author zouyb
 * rsa加密工具类。
 */
public class RsaUtils {

    private static String USE_RSA_ENCRYPT_JSON = "yes";

    //这应该生成文件存储在文件里面，而不是写在java中。
    private static String privateKeyStr ="MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDC2zWVzSS2O/z2eio6ZuFGGjWlavqnjCKz608ZNyl01MYtdOnfgMjJa0IjadYrY9+ECWaxL5J6dm3wqUfuOuudJ6rjFvhQJ5TfSr/8nVwm4z3vHKrTxmnCKn0umTTVj1MHtFM30UoQoALNQOAa4am6ZpYgcNyfWE9uRd7OwiviNXCU/dUDK6R9GF/3+0rBUHzFRIwxzyFUk3Nh4C4NFW/74pad22HL6KstejRw6U98SrcB3sWpZ/F3Y+X1ZStmnGLyWTqTGTEiFKjfoziYAayuUIoM4hadcVzwedrHGClCg5gsQ9sbnkfCQycAhvqY2VbztNvr0S6G3XA4l7" +
            "dZAMT1AgMBAAECggEAV5jgUAW3GU2CfPOShEq3vyINN94N7MBs4WFJX9aShnkY1lqWwOlOWm4jf3QW2c6+LsdjKQhv2yneZrBTK9/FCBNLGWe8HZyNqowaHuBHCxxhPVsv2KB77tPXDtnFDLSv6OjB+HfXyf2FBOzDUpJcEPY" +
            "JS3AxXlj+7F3wuhZEUB5ZHVFVXqP/cNr7FuC7cdbO1ueyzVYcZfA4qVy3TbTor1rU/w1grhBY7OtztrBHYG0SQmALgczXdfYVIe913VKTF7uFrMdJSwkW+DQVJV1iXhF7J8o8vj0g+5dIVq5/Emzebitzs48GiG2fuMndD+7" +
            "ypuyYqIL3Y6E7Duhz+TeDwQKBgQDnP8V1qACVSaX+LsGPF4t+siausdDy+aO3BhXgDuJZmViqWHqImFj+EETA+DLOSAew22nJq8e99LuEsmWkwFbtrfcWpJ14q8XzN0xTs6fdogkW3X/siBIwG8OOEq4tN+0PUexL0ziYIjw" +
            "UrEK84weB0KxDclXX4p5f7IuKvKHVGQKBgQDXtkYzHHSRpw+ji/xigebFIOAoSYnnsc4n2Nmb8swrbYI4plQlsDCJ6XhjB8nl+ZJDYPwIQtukOBvU+9ieHuzXqRXazQ5x8CkwlJ+O+ngDvmh+LK1+qNzYMTbvPpTtsjy3S85" +
            "b51mxtxrLcH4YQwBDKPvGJwrVAvPp8SDhraquPQKBgQCFyaNX+qa7uCxM7ogVXS3bUv6GO2ID8OIC1YZ6ZqIV7F4bz6y0sk9qTcc5TV83cPziGU8O2k9pWtnSX9PmiVDBmdBGTV69ncOcfvWtx1brJv/TNrr4AZFkO+vbmns" +
            "itiR0pJTtGXq4qbXQ0Aicd0y0kiHXs6JukDHIlXX2AkQuoQKBgHwri1tOjPdu4Dy3jw+xUQK2evIV6tE5c79SP2laqRZykhxD+no7YqVwhIEU8a51LDGm+U1UFxT/y12dn0Ld8F3wEgmCMkd6BGvXkcV+/mjBSWsgErqvP3kf" +
            "U00CjD4LdJHajC4xDVuUWFM1LRCQOO1wAQY+YSje60MGL3RZQL/NAoGBAK8AzmxyHvCd3qXbxvMqz7n5LJYIqLNk54rpyEgbI4F/nVcjo53Gs3aMcojBYd4mBiG7EpJhfZrXBRY57JX96PlWcSIFo7gwstk7yP8IjGIrePBK" +
            "7B9usOnb+DGL0S5KT0bZG4f4q9OgiNeh/gzAM0OXn/GuHMghnmfQzbwl6nXh";

    private static String publicKeyStr="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwts1lc0ktjv89noqOmbhRho1pWr6p4wis+tPGTcpdNTGLXTp34DIyWtCI2nWK2PfhAlmsS+SenZt8KlH7jrrnSeq4xb4UCeU30q//J1cJuM97xyq08Zpwip9Lpk01Y9TB7RTN9FKEKACzUDgGuGpumaWIHDcn1hPbkXezsIr4jVwlP3VAyukfRhf9/tKwVB8xUSMMc8hVJNzYeAuDRVv++KWndthy+irLXo0cOlPfEq3Ad7FqWfxd2Pl9WUrZpxi8lk6kxkxIhSo36M4mAGsrlCKDOIWnXFc8HnaxxgpQoOYLEPbG55HwkMnAIb6mNlW87Tb69Euht1wOJe3WQDE9QIDAQAB";

    /**
     * rsa 加密的调用接口。
     * @param o：加密的对象。
     * @param clazz：需要加密的class对象。
     */
    public static void rsaObject(Object o,Class clazz) throws Exception {

        //配置的总开关，默认从数据库中获取，可以对这个工具类实现打开和关闭效果。
        if(!"yes".equalsIgnoreCase(USE_RSA_ENCRYPT_JSON)){
            return;
        }

        //非空判断。程序的健全性校验。
        if(Objects.isNull(o)){
            return;
        }

        //判断当前对象是否有父类，并且父类不是Object。
        if(!clazz.getSuperclass().equals(Object.class)){
            rsaObject(o,clazz.getSuperclass());
        }

        //调用加密方法加密当前类。
        rsaThisObject(o,clazz);
    }

    /**
     * 实际的加密方法
     * @param o
     * @param clazz
     */
    private static void rsaThisObject(Object o, Class clazz) throws Exception {
        Field[] fields = clazz.getDeclaredFields();

        //健全性校验
        if(Objects.isNull(o)){
            return;
        }

        //循环遍历字段。
        for (Field field : fields) {
            FieldWithRsa fieldWithRsa = field.getAnnotation(FieldWithRsa.class);
            //如果字段注解不为空。
            if(fieldWithRsa != null){
                //加密校验。
                String getterMethod = getMethodName(field.getName(),fieldWithRsa,"get");
                Method method = clazz.getMethod(getterMethod);
                //需要用户做规则，只能加密String类型的字段。
                String invoke = (String) method.invoke(o);
                //rsa的keypair采用2048位的只能加密原文小于240字节的文本。
                if (invoke != null && !"".equalsIgnoreCase(invoke) && invoke.length() <= 200) {
                    String encrypt = encrypt(invoke,getPubKey());
                    String setMethodName = getMethodName(field.getName(),fieldWithRsa,"set");
                    Method setMethod = clazz.getMethod(setMethodName,String.class);
                    setMethod.invoke(o,encrypt);
                    //尝试自己解密。
                    System.out.println("孩子不懂事，解着玩的："+decrypt(encrypt,getPrivateKey()));
                }
            }

            ObjectWithRsa objectWithRsa = field.getAnnotation(ObjectWithRsa.class);
            //如果对象注解不为空
            if(objectWithRsa != null){
                Class fieldClass = objectWithRsa.clazz();
                String getMethod = getMethodName(field.getName(),objectWithRsa,"get");
                Method method = clazz.getMethod(getMethod);
                Object fieldObject = method.invoke(o);
                //兼容集合类型。
                if(fieldObject instanceof Collection){
                    Collection c = (Collection) fieldObject;
                    Iterator iterator = c.iterator();
                    while (iterator.hasNext()){
                        rsaThisObject(iterator.next(),fieldClass);
                    }
                }else if(fieldObject instanceof Map){
                    Map map = (Map) fieldObject;
                    Iterator iterator = map.keySet().iterator();
                    while (iterator.hasNext()){
                        rsaThisObject(map.get(iterator.next()),fieldClass);
                    }
                }else{
                    //普通对象
                    rsaThisObject(fieldObject,fieldClass);
                }
            }
        }
    }

    private static String encrypt(String text, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedBytes = cipher.doFinal(text.getBytes());
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    /**
     * 生成公钥和私钥的方法
     * @throws NoSuchAlgorithmException
     */
    public static void main(String[] args) throws NoSuchAlgorithmException {
        KeyPairGenerator rsa = KeyPairGenerator.getInstance("RSA");
        rsa.initialize(2048);
        KeyPair keyPair = rsa.generateKeyPair();
        PrivateKey aPrivate = keyPair.getPrivate();
        PublicKey aPublic = keyPair.getPublic();
        byte[] miyao = aPrivate.getEncoded();
        byte[] gongyyao = aPublic.getEncoded();
        String privateKey = Base64.getEncoder().encodeToString(miyao);
        String publicKey = Base64.getEncoder().encodeToString(gongyyao);
        // 打印私钥的Base64编码字符串
        System.out.println(privateKey);
        System.out.println("----------------------------------");
        // 打印公钥的Base64编码字符串
        System.out.println(publicKey);
    }

    private static String decrypt(String text, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(text));
        String de = new String(decryptedBytes);
        return de;
    }

    private static String getMethodName(String name, Annotation annotation, String beanType) {
        String methodName = "";
        String annoGet = "";
        String annoSet = "";

        if(annotation instanceof FieldWithRsa){
            FieldWithRsa a = (FieldWithRsa) annotation;
            annoGet = a.getter();
            annoSet = a.setter();
        }
        if(annotation instanceof ObjectWithRsa){
            ObjectWithRsa a = (ObjectWithRsa) annotation;
            annoGet = a.getter();
        }
        if("get".equalsIgnoreCase(beanType)){
            if(null != annoGet && !"".equalsIgnoreCase(annoGet)){
                return annoGet;
            }else{
                return "get"+name.substring(0,1).toUpperCase()+name.substring(1);
            }

        }
        if("set".equalsIgnoreCase(beanType)){
            if(null != annoSet && !"".equalsIgnoreCase(annoSet)){
                return annoSet;
            }else{
                return "set"+name.substring(0,1).toUpperCase()+name.substring(1);
            }
        }
        return methodName;
    }


    /**
     * 根据BASE64的内容转变成PublicKey
     * @return
     */
    private static PublicKey getPubKey() {
        PublicKey publicKey = null;
        try {
            // 自己的公钥
            java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec(
                    new BASE64Decoder().decodeBuffer(publicKeyStr));
            // RSA对称加密算法
            KeyFactory keyFactory;
            keyFactory = KeyFactory.getInstance("RSA");
            // 取公钥匙对象
            publicKey = keyFactory.generatePublic(bobPubKeySpec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return publicKey;
    }

    /**
     * 根据BASE64获取PrivateKey
     * @return
     */
    private static PrivateKey getPrivateKey() {
        PrivateKey privateKey = null;
        PKCS8EncodedKeySpec priPKCS8;
        try {
            priPKCS8 = new PKCS8EncodedKeySpec(
                    new BASE64Decoder().decodeBuffer(privateKeyStr));
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            privateKey = keyf.generatePrivate(priPKCS8);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return privateKey;
    }
}
